﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace Utility.Helper
{

    public enum SequentialGuidType
    {
        /// <summary>
        /// 生成的GUID 按照字符串顺序排列
        /// </summary>
        SequentialAsString,
        /// <summary>
        /// 生成的GUID 按照二进制的顺序排列
        /// </summary>
        SequentialAsBinary,
        /// <summary>
        /// 生成的GUID 像SQL Server, 按照末尾部分排列
        /// </summary>
        SequentialAtEnd
    }

    public static class GuidExtension
    {

        private static readonly object cLock = new object();
        private static readonly RNGCryptoServiceProvider RandomGenerator = new RNGCryptoServiceProvider();

        /// <summary>
        /// 生成有序的唯一的Guid，数据库聚集索引提高查询效率
        /// </summary>
        /// <param name="guidType">默认按照数据库</param>
        /// <returns></returns>
        public static Guid NewGuid(SequentialGuidType guidType = SequentialGuidType.SequentialAtEnd)
        {
            lock (cLock)
            {
                byte[] randomBytes = new byte[10];
                RandomGenerator.GetBytes(randomBytes);
                long timestamp = DateTime.UtcNow.Ticks / 10000L;
                byte[] timestampBytes = BitConverter.GetBytes(timestamp);

                if (BitConverter.IsLittleEndian)
                    Array.Reverse(timestampBytes);

                byte[] guidBytes = new byte[16];

                switch (guidType)
                {
                    case SequentialGuidType.SequentialAsString:
                    case SequentialGuidType.SequentialAsBinary:

                        Buffer.BlockCopy(timestampBytes, 2, guidBytes, 0, 6);
                        Buffer.BlockCopy(randomBytes, 0, guidBytes, 6, 10);

                        if (guidType == SequentialGuidType.SequentialAsString && BitConverter.IsLittleEndian)
                        {
                            Array.Reverse(guidBytes, 0, 4);
                            Array.Reverse(guidBytes, 4, 2);
                        }

                        break;

                    case SequentialGuidType.SequentialAtEnd:

                        Buffer.BlockCopy(randomBytes, 0, guidBytes, 0, 10);
                        Buffer.BlockCopy(timestampBytes, 2, guidBytes, 10, 6);
                        break;
                }

                return new Guid(guidBytes);
            }

           
        }


      
    }
}
